pre-init config and hook
authorJoey Hess <joeyh@joeyh.name>
Mon, 13 Jan 2025 18:22:49 +0000 (14:22 -0400)
committerJoey Hess <joeyh@joeyh.name>
Mon, 13 Jan 2025 18:22:49 +0000 (14:22 -0400)
Added annex.pre-init-command git config and pre-init-annex hook that is run
before git-annex repository initialization.

This can block initialization. Or it can preform pre-initialization
configuration or tweaking.

I left stdio connected while it's running, so it could also be used for
interactive prompting conceivably, although that would want to use /dev/tty
anyway probably in order to not pollute the stdout of a command when
automatic initialization is done.

Sponsored-by: Dartmouth College's OpenNeuro project
Annex/Hook.hs
Annex/Init.hs
CHANGELOG
Types/GitConfig.hs
doc/git-annex-init.mdwn
doc/git-annex.mdwn
doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks.mdwn
doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_5_c1687c1d853159debff18ed5c75268ad._comment [new file with mode: 0644]
doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_6_e2e93b900f018d276029389405a09c28._comment [new file with mode: 0644]

index e4264ce9d52e2aaceb0ab5d0cbc061313e5e625a..0496094be88ec1d2117d5da42ce397fac637a425 100644 (file)
@@ -48,6 +48,9 @@ preCommitAnnexHook = Git.Hook "pre-commit-annex" "" []
 postUpdateAnnexHook :: Git.Hook
 postUpdateAnnexHook = Git.Hook "post-update-annex" "" []
 
+preInitAnnexHook :: Git.Hook
+preInitAnnexHook = Git.Hook "pre-init-annex" "" []
+
 freezeContentAnnexHook :: Git.Hook
 freezeContentAnnexHook = Git.Hook "freezecontent-annex" "" []
 
@@ -98,20 +101,33 @@ doesAnnexHookExist hook = do
                        return exists
 
 runAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex ()
-runAnnexHook hook commandcfg = ifM (doesAnnexHookExist hook)
+runAnnexHook hook commandcfg = runAnnexHook' hook commandcfg >>= \case
+       Nothing -> noop
+       Just failedcommanddesc -> 
+               warning $ UnquotedString $ failedcommanddesc ++ " failed"
+
+-- Returns Nothing if the hook or GitConfig command succeeded, or a
+-- description of what failed.
+runAnnexHook' :: Git.Hook -> (GitConfig -> Maybe String) -> Annex (Maybe String)
+runAnnexHook' hook commandcfg = ifM (doesAnnexHookExist hook)
        ( runhook
        , runcommandcfg
        )
   where
-       runhook = unlessM (inRepo $ Git.runHook boolSystem hook []) $ do
-               h <- fromRepo $ Git.hookFile hook
-               commandfailed h
+       runhook = ifM (inRepo $ Git.runHook boolSystem hook [])
+               ( return Nothing
+               , do
+                       h <- fromRepo (Git.hookFile hook)
+                       commandfailed h
+               )
        runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
+               Nothing -> return Nothing
                Just command ->
-                       unlessM (liftIO $ boolSystem "sh" [Param "-c", Param command]) $
-                               commandfailed command
-               Nothing -> noop
-       commandfailed c = warning $ UnquotedString $ c ++ " failed"
+                       ifM (liftIO $ boolSystem "sh" [Param "-c", Param command])
+                               ( return Nothing
+                               , commandfailed $ "git configured command '" ++  command ++ "'"
+                               )
+       commandfailed c = return $ Just c
 
 runAnnexPathHook :: String -> Git.Hook -> (GitConfig -> Maybe String) -> RawFilePath -> Annex Bool
 runAnnexPathHook pathtoken hook commandcfg p = ifM (doesAnnexHookExist hook)
@@ -121,9 +137,9 @@ runAnnexPathHook pathtoken hook commandcfg p = ifM (doesAnnexHookExist hook)
   where
        runhook = inRepo $ Git.runHook boolSystem hook [ File (fromRawFilePath p) ]
        runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
+               Nothing -> return True
                Just basecmd -> liftIO $
                        boolSystem "sh" [Param "-c", Param $ gencmd basecmd]
-               Nothing -> return True
        gencmd = massReplace [ (pathtoken, shellEscape (fromRawFilePath p)) ]
 
 outputOfAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex (Maybe String)
@@ -135,6 +151,6 @@ outputOfAnnexHook hook commandcfg = ifM (doesAnnexHookExist hook)
        runhook = inRepo (Git.runHook runhook' hook [])
        runhook' c ps = Just <$> readProcess c (toCommand ps)
        runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
+               Nothing -> return Nothing
                Just command -> liftIO $ 
                        Just <$> readProcess "sh" ["-c", command]
-               Nothing -> return Nothing
index 0cb2e090190ffb7b120c3c90ed4b1ba2c4b06a82..ea7cd09765fb5479d3aa57d57c09cf2bb6df2196 100644 (file)
@@ -1,6 +1,6 @@
 {- git-annex repository initialization
  -
- - Copyright 2011-2024 Joey Hess <id@joeyh.name>
+ - Copyright 2011-2025 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -74,17 +74,29 @@ data InitializeAllowed = InitializeAllowed
 
 checkInitializeAllowed :: (InitializeAllowed -> Annex a) -> Annex a
 checkInitializeAllowed a = guardSafeToUseRepo $ noAnnexFileContent' >>= \case
-       Nothing -> do
-               checkSqliteWorks
-               a InitializeAllowed
+       Nothing -> runAnnexHook' preInitAnnexHook annexPreInitCommand >>= \case
+               Nothing -> do
+                       checkSqliteWorks
+                       a InitializeAllowed
+               Just failedcommanddesc -> do
+                       initpreventedby failedcommanddesc
+                       notinitialized
        Just noannexmsg -> do
-               warning "Initialization prevented by .noannex file (remove the file to override)"
+               initpreventedby ".noannex file (remove the file to override)"
                unless (null noannexmsg) $
                        warning (UnquotedString noannexmsg)
-               giveup "Not initialized."
+               notinitialized
+  where
+       initpreventedby r = warning $ UnquotedString $
+               "Initialization prevented by " ++ r
+       notinitialized = giveup "Not initialized."
 
 initializeAllowed :: Annex Bool
-initializeAllowed = isNothing <$> noAnnexFileContent'
+initializeAllowed = noAnnexFileContent' >>= \case
+       Nothing -> runAnnexHook' preInitAnnexHook annexPreInitCommand >>= \case
+               Nothing -> return True
+               Just _ -> return False
+       Just _ -> return False
 
 noAnnexFileContent' :: Annex (Maybe String)
 noAnnexFileContent' = inRepo $
@@ -268,7 +280,7 @@ autoInitialize' check startupannex remotelist =
        getInitializedVersion >>= maybe needsinit checkUpgrade
   where
        needsinit =
-               whenM (initializeAllowed <&&> check) $ do
+               whenM (check <&&> initializeAllowed) $ do
                        initialize startupannex Nothing Nothing
                        autoEnableSpecialRemotes remotelist
 
index 101fb7d0e2d0f68db69716145125169807ac8526..d7ca6e3d15d811c589e0daa5dedadc5710840fe7 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -22,6 +22,8 @@ git-annex (10.20250103) UNRELEASED; urgency=medium
     annex.http-headers-command.
   * Added git configs annex.post-update-command and annex.pre-commit-command
     that correspond to the post-update-annex and pre-commit-annex hooks.
+  * Added annex.pre-init-command git config and pre-init-annex hook
+    that is run before git-annex repository initialization.
 
  -- Joey Hess <id@joeyh.name>  Fri, 03 Jan 2025 14:30:38 -0400
 
index 81904653f86f62dcd66e00f5a1d081ca66241734..053a9c8c663e1489f29efce9cf4a85614efea0ed 100644 (file)
@@ -97,6 +97,7 @@ data GitConfig = GitConfig
        , annexAlwaysCompact :: Bool
        , annexCommitMessage :: Maybe String
        , annexCommitMessageCommand :: Maybe String
+       , annexPreInitCommand :: Maybe String
        , annexPreCommitCommand :: Maybe String
        , annexPostUpdateCommand :: Maybe String
        , annexMergeAnnexBranches :: Bool
@@ -192,6 +193,7 @@ extractGitConfig configsource r = GitConfig
        , annexAlwaysCompact = getbool (annexConfig "alwayscompact") True
        , annexCommitMessage = getmaybe (annexConfig "commitmessage")
        , annexCommitMessageCommand = getmaybe (annexConfig "commitmessage-command")
+       , annexPreInitCommand = getmaybe (annexConfig "pre-init-command")
        , annexPreCommitCommand = getmaybe (annexConfig "pre-commit-command")
        , annexPostUpdateCommand = getmaybe (annexConfig "post-update-command")
        , annexMergeAnnexBranches = getbool (annexConfig "merge-annex-branches") True
index aa0e4186391d8b53e3b1a81e38ea51479472e233..dcbfd64102994d8f787a218eca70746488d010c2 100644 (file)
@@ -27,7 +27,11 @@ already initialized git-annex repository.
 A top-level `.noannex` file will prevent git-annex init from being used
 in a repository. This is useful for repositories that have a policy
 reason not to use git-annex. The content of the file will be displayed
-to the user who tries to run git-annex init.
+to the user who tries to run git-annex init. 
+
+The annex.pre-init-command git configuration or pre-init-annex hook
+is run before initialization and can exit nonzero as well to prevent
+initialization, as well as doing other setup.
 
 # EXAMPLES
 
index db305956f3a13d762d757c2b222296a4b2e7bbfb..869686883ae81255792f932ebb2abd37ceffba2d 100644 (file)
@@ -1176,6 +1176,16 @@ repository, using [[git-annex-config]]. See its man page for a list.)
   Alternatively, a hook script can be installed in
   `.git/hooks/pre-commit-annex`
 
+* `annex.pre-init-command`
+
+  This command is run before the repository is initialized, either by
+  `git-annex init`, or automatic initialization. It can configure
+  the repository in any way needed. If it exits nonzero, the repository
+  initialization will fail.
+  
+  Alternatively, a hook script can be installed in
+  `.git/hooks/pre-init-annex`
+
 * `annex.alwayscompact`
 
   By default, git-annex compacts data it records in the git-annex branch.
index 95cd81a9c16ad1d6cbf2357d8ccbe6bb47758dd5..3d6d7c97f25688476850239c7ac01f1b69433c9a 100644 (file)
@@ -6,3 +6,5 @@ The idea is stemmed from discussions/problems with using freeze/thaw hooks, and
 
 [[!meta author=yoh]]
 [[!tag projects/openneuro]]
+
+> [[done]] --[[Joey]]
diff --git a/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_5_c1687c1d853159debff18ed5c75268ad._comment b/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_5_c1687c1d853159debff18ed5c75268ad._comment
new file mode 100644 (file)
index 0000000..554fb73
--- /dev/null
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 5"""
+ date="2025-01-13T17:33:02Z"
+ content="""
+Note that the `.noannex` file that prevents init has some overlap with
+a pre-init hook that exits nonzero. I guess the .noannex file has the
+benefit of working in every clone of a repository without additional
+configuration.
+"""]]
diff --git a/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_6_e2e93b900f018d276029389405a09c28._comment b/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_6_e2e93b900f018d276029389405a09c28._comment
new file mode 100644 (file)
index 0000000..a40031a
--- /dev/null
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 6"""
+ date="2025-01-13T18:17:42Z"
+ content="""
+Implemented .git/hooks/pre-init-annex 
+(and alternatively git config annex.pre-init-command)
+
+Note that this is also run before automatic initialization.
+"""]]